package ldh.maker.component;

import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import ldh.database.*;
import ldh.maker.code.CreateCode;
import ldh.maker.controller.CreateEnumController;
import ldh.maker.database.TableInfo;
import ldh.maker.db.EnumDb;
import ldh.maker.db.PojoDb;
import ldh.maker.db.SettingDb;
import ldh.maker.freemaker.EnumStatusMaker;
import ldh.maker.freemaker.PojoMaker;
import ldh.maker.handle.DbHandle;
import ldh.maker.handle.ProjectTreeHandle;
import ldh.maker.util.*;
import ldh.maker.vo.DBConnectionData;
import ldh.maker.vo.SettingData;
import ldh.maker.vo.TreeNode;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.sql.*;
import java.util.*;
import java.util.List;

/**
 * Created by ldh on 2017/3/25.
 */
public class ColumnUi extends ScrollPane {

    ValidationSupport validationSupport = new ValidationSupport();

    protected String dbName;
    protected String tableName;
    protected TreeItem<TreeNode> treeItem;
    protected Table table = null;
    protected CodeUi codeUi;

    protected Map<String, TextField> beanFieldMap = new HashMap<>();
    protected Map<String, TextField> beanTextFieldMap = new HashMap<>();

    protected Map<String, TextField> columnFieldMap = new HashMap<>();
    protected Map<String, TextField> columnTextFieldMap = new HashMap<>();
    protected Map<String, ChoiceBox<String>> choiceTypeMap = new HashMap<>();
    protected Map<String, CheckBox> functionMap = new HashMap<>();

    public ColumnUi(TreeItem<TreeNode> treeItem, String dbName, String tableName, CodeUi codeUi) {
        this.dbName = dbName;
        this.treeItem = treeItem;
        this.tableName = tableName;
        this.codeUi = codeUi;
        initColumn();
    }

    private void initColumn() {
        TitledPane tp = new TitledPane();
        tp.setText("设置pojo对象");

        GridPane gridPane = new GridPane();
        gridPane.setStyle("-fx-background-color: white");
        gridPane.getColumnConstraints().add(new ColumnConstraints(40));
        gridPane.getColumnConstraints().add(new ColumnConstraints(100));
        gridPane.getColumnConstraints().add(new ColumnConstraints(150));
        gridPane.getColumnConstraints().add(new ColumnConstraints(10));
        gridPane.getColumnConstraints().add(new ColumnConstraints(60));
        gridPane.getColumnConstraints().add(new ColumnConstraints(150));
        gridPane.getColumnConstraints().add(new ColumnConstraints(200));
        gridPane.setPadding(new Insets(10, 10, 10, 10));
        gridPane.setVgap(10);gridPane.setHgap(10);

        int row = 0;
        try {
            Table table = this.getTable();
            List<Column> columns = table.getColumnList();
            addHead(gridPane, row++);
            addTable(gridPane, row++, table);
            for (Column column : columns) {
                Set<String> types = new HashSet<>(DbUtil.javaType());
                addColumn(gridPane, row++, column, types, column.getProperty());
            }

            ScrollPane scrollPane = new ScrollPane(gridPane);
            scrollPane.setStyle("-fx-background-color: white");
            tp.setContent(scrollPane);

            TitledPane tp2 = new TitledPane();
            tp2.setText("设置功能");
            VBox functionPane = createFunctionPane();
            tp2.setContent(functionPane);

            HBox savePane = createSavePane();
            VBox vBox = new VBox();
            vBox.getChildren().addAll(tp, tp2, savePane);
            this.setContent(vBox);
            this.setFitToWidth(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
//        addBtn(gridPane, row++);
    }

    private void addColumn(GridPane gridPane, int row, Column column, Set<String> propertyType, String propertyName) {
        int c=0;
        gridPane.add(new Label("列"), c++, row);
        TextField columnTypeField = new TextField(column.getType());
        columnTypeField.setEditable(false);
        columnTypeField.setStyle("-fx-background-color: #DDDDDD");
        gridPane.add(columnTypeField, c++, row);

        TextField columnNameField = new TextField(column.getName());
        columnNameField.setEditable(false);
        columnNameField.setStyle("-fx-background-color: #DDDDDD");
        gridPane.add(columnNameField, c++, row);

        gridPane.add(new Label(""), c++, row);

        String columnType = columnType(column);
//        propertyType.add(columnType);
        ChoiceBox<String> types = new ChoiceBox(FXCollections.observableArrayList(propertyType));
        types.getSelectionModel().select(columnType);
        choiceTypeMap.put(column.getName(), types);
        gridPane.add(types, c++, row);

        propertyName = column.getWapClass() == null ? propertyName :  column.getWapClass().getShowText();
        TextField propertyNameField = new TextField(propertyName);
        validationSupport.registerValidator(propertyNameField, Validator.createEmptyValidator("属性名称不能为空"));
        columnFieldMap.put(column.getName(), propertyNameField);
        gridPane.add(propertyNameField, c++, row);

        TextField propertyTextField = new TextField();
        if (column.getText() != null) {
            propertyTextField.setText(column.getText());
        }
        validationSupport.registerValidator(propertyTextField, Validator.createEmptyValidator("文本内容不能为空"));
        columnTextFieldMap.put(column.getName(), propertyTextField);
        gridPane.add(propertyTextField, c++, row);

        final int cc = c;
        types.getSelectionModel().selectedItemProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue)->{
            typeChangeEvent(gridPane, types, cc, row, column, propertyNameField);
        });
        if (column.getPropertyClass() == Enum.class) {
            Button b = new Button("build Enum");
            b.setOnAction(e->openEnumDialog(column));
            gridPane.add(b, cc, row);
        }
    }

    private String columnType(Column column) {
//        if (column.getWapClass() != null) {
//            return column.getForeignKey().getForeignTable().getJavaName();
//        }
        return column.getPropertyClass().getSimpleName();
    }

    private void typeChangeEvent(GridPane gridPane, ChoiceBox<String> types, int col, int row,
                                  Column column, TextField propertyNameField) {
        String newValue = types.getSelectionModel().getSelectedItem();
        String columnType = columnType(column);
        if(newValue.equals(column.getJavaType())) {
            propertyNameField.setText(column.getProperty());
        } else if (column.getWapClass() != null && newValue.equals(columnType)) {
            propertyNameField.setText(column.getWapClass().getShowText());
        } else if (newValue.equals("Enum")) {
            Button b = new Button("build Enum");
            b.setOnAction(e->openEnumDialog(column));
            gridPane.add(b, col, row);
            openEnumDialog(column);
        }
    }

    private void openEnumDialog(Column column) {
        Parent root = null;
        try {
            Stage stage = new Stage();
            stage.initOwner(UiUtil.STAGE);
            FXMLLoader loader = new FXMLLoader(DbHandle.class.getResource("/fxml/CreateEnumForm.fxml"));
            root = loader.load();
            CreateEnumController controller = loader.getController();
            Scene scene = new Scene(root);
            stage.setFullScreen(false);
            stage.setResizable(false);
            stage.setTitle("创建枚举");
            stage.setScene(scene);


            String key = treeItem.getParent().getValue().getId() + "_" + dbName + "_" + tableName + "_" + column.getName();

            controller.setTreeItem(treeItem);
            controller.setData(column, table, dbName);
            controller.setKey(key);
            controller.setCallback(table-> {
                saveData();
                return null;
            });

            SettingData data = SettingDb.loadData(treeItem.getParent().getValue(), dbName);
            if (data != null) {
                controller.setPackage(getProjectRootPackage(data.getPojoPackageProperty()) + ".constant");
            }
            stage.show();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    private String getProjectRootPackage(String pojoPackage) {
        int t = pojoPackage.lastIndexOf(".");
        return pojoPackage.substring(0, t);
    }

    private void addTable(GridPane gridPane, int row, Table table) {
        gridPane.add(new Label("表名称"), 0, row);

        TextField tableField = new TextField(tableName);
        tableField.setEditable(false);
        tableField.setStyle("-fx-background-color: #DDDDDD");
        gridPane.add(tableField, 1, row, 2, 1);

        ChoiceBox<String> types = new ChoiceBox(FXCollections.observableArrayList("Object"));
        types.getSelectionModel().select("Object");
        gridPane.add(types, 4, row, 1, 1);

        TextField beanField = new TextField(table.getJavaName());
        validationSupport.registerValidator(beanField, Validator.createEmptyValidator("对象名称不能为空"));
        beanFieldMap.put(tableName, beanField);
        gridPane.add(beanField, 5, row, 1, 1);

        TextField textField = new TextField(table.getText() == null ? "" : table.getText());
        validationSupport.registerValidator(textField, Validator.createEmptyValidator("对象的文本内容不能为空"));
        beanTextFieldMap.put(tableName, textField);
        gridPane.add(textField, 6, row, 1, 1);
    }

    private void addHead(GridPane gridPane, int row) {
        gridPane.add(new Label("表属性"), 0, row, 3, 1);
        gridPane.add(new Label("POJO类型"), 4, row, 1, 1);
        gridPane.add(new Label("POJO名称"), 5, row, 1, 1);
        gridPane.add(new Label("POJO文本内容"), 6, row, 1, 1);
    }

    protected Table getTable() throws SQLException {
        if (table == null) {
            TableInfo tableInfo = DbInfoFactory.getInstance().get(treeItem.getValue().getParent().getId() + "_" + dbName);
            table = tableInfo.getTable(tableName);
            PojoDb.loadData(treeItem.getValue().getParent(), dbName, table);
            if (EnumFactory.getInstance().getEnumStatusMakerMap().size() < 1) {
                EnumDb.loadData(treeItem.getValue().getParent().getId(), dbName);
            }
        }
        return table;
    }

    private HBox createSavePane() {
        HBox hbox = new HBox();
        hbox.setPadding(new Insets(10, 10, 10, 10));
        hbox.setStyle("-fx-background-color: white");
        hbox.setSpacing(10);
        Region region = new Region();
        Button saveBtn = new Button("保存并生成代码");
        saveBtn.getStyleClass().add("btn");
        saveBtn.getStyleClass().add("btn-primary");
        hbox.getChildren().addAll(region, saveBtn);
        HBox.setHgrow(region, Priority.ALWAYS);
        saveBtn.setOnAction(e->{
            if(validationSupport.isInvalid()) {
                DialogUtil.show(Alert.AlertType.WARNING, "填写错误", "请按照要求填写");
                return;
            }
            saveAndCreateData();
        });
        return hbox;
    }

    private Table saveData() {
        try {
            Table table = getTable();
            table.setJavaName(beanFieldMap.get(table.getName()).getText().trim());
            table.setText(beanTextFieldMap.get(table.getName()).getText().trim());
            for (Column column : table.getColumnList()) {
                column.setProperty(columnFieldMap.get(column.getName()).getText().trim());
                column.setText(columnTextFieldMap.get(column.getName()).getText().trim());
                String type = choiceTypeMap.get(column.getName()).getSelectionModel().getSelectedItem();
                Class clazz = DbUtil.javaClass(type);
                if (clazz != null) {
                    column.setJavaType(clazz);
                } else if (column.getWapClass() != null){
                    column.setWap(true);
                }
            }
            for(Map.Entry<String, CheckBox> entry : functionMap.entrySet()) {
                MakerConfig.getInstance().getFunctionMap(tableName).put(entry.getKey(), entry.getValue().isSelected());
            }
            PojoDb.saveOrUpdate(dbName, table, treeItem.getValue().getParent().getId());
            return table;
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        return null;
    }

    private void saveAndCreateData() {
        try {
            Table table = saveData();
            codeUi.createCode(table, true);
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
    }

    private VBox createFunctionPane() throws SQLException {
        Table table = getTable();
        Map<String, Boolean> funs = MakerConfig.getInstance().getFunctionMap(tableName);
        if (funs == null || funs.size() == 0) {
            funs = new TreeMap<>();
            MakerConfig.getInstance().addFunctionMap(tableName, funs);
        }
        VBox vBox = new VBox();
        vBox.getChildren().addAll(new Label("基本操作"), new Separator(Orientation.HORIZONTAL));
        FlowPane flowPane = createFlowPane();
        createCheckBox(flowPane, "insert", true);
        createCheckBox(flowPane, "insertSelective", true);
        for (UniqueIndex ui : table.getIndexies()) {
            boolean isCreate = FreeMakerUtil.canCreate(ui) && !table.isMiddle();
            String uiname = FreeMakerUtil.uniqueName(ui);
            createCheckBox(flowPane, "getBy" + uiname, isCreate);
            createCheckBox(flowPane, "updateBy" + uiname, isCreate);
            createCheckBox(flowPane, "updateNotNullBy" + uiname, isCreate);
            createCheckBox(flowPane, "deleteBy" + uiname, isCreate);
        }
        vBox.getChildren().add(flowPane);

        vBox.getChildren().addAll(new Label("查询多对一关联表:"), new Separator(Orientation.HORIZONTAL));
        FlowPane flowPane3 = createFlowPane();
        for (UniqueIndex ui : table.getIndexies()) {
            String uiname = FreeMakerUtil.uniqueName(ui);
            String un = "By" + uiname;
            boolean isCreate = FreeMakerUtil.canCreate(ui) && !table.isMiddle();

            for (ForeignKey fk : table.getForeignKeys()) {
                createCheckBox(flowPane3, "getWith" + FreeMakerUtil.firstUpper(fk.getColumn().getProperty()) + un, false);
                createCheckBox(flowPane3, "getJoin" + FreeMakerUtil.firstUpper(fk.getColumn().getProperty()) + un, false);
            }
            if (table.getForeignKeys().size() > 1) {
                createCheckBox(flowPane3, "getWithAssociates" + un, isCreate);
                createCheckBox(flowPane3, "getJoinAssociates" + un, isCreate);
            }
        }
        vBox.getChildren().add(flowPane3);

        FlowPane flowPane4 = createFlowPane();
        vBox.getChildren().addAll(new Label("查询一对多关联表:"), new Separator(Orientation.HORIZONTAL));
        for (UniqueIndex ui : table.getIndexies()) {
            String uiname = FreeMakerUtil.uniqueName(ui);
            String un = "By" + uiname;
            boolean isCreate = FreeMakerUtil.canCreate(ui) && !table.isMiddle();

            for (ForeignKey fk : table.getMany()) {
                createCheckBox(flowPane4, "getJoin" + FreeMakerUtil.firstUpper(fk.getTable().getJavaName()) + un, isCreate);
            }
            if (table.getMany().size() > 1) {
                createCheckBox(flowPane4, "getJoinAll" + un, false);
            }
        }
        vBox.getChildren().add(flowPane4);

        FlowPane flowPane5 = createFlowPane();
        vBox.getChildren().addAll(new Label("查询多对多关联表:" ), new Separator(Orientation.HORIZONTAL));
        for (UniqueIndex ui : table.getIndexies()) {
            String uiname = FreeMakerUtil.uniqueName(ui);
            String un = "By" + uiname;
           for (ManyToMany mtm : table.getManyToManys()) {
                boolean isCreate2 = false;
                if (ui.isPrimaryKey() && !table.isMiddle()) {
                    isCreate2 = true;
                }
                createCheckBox(flowPane5, "getMany" + FreeMakerUtil.firstUpper(mtm.getSecondTable().getJavaName()) + "s" + un, isCreate2);
            }
            if (table.getManyToManys().size() > 1) {
                createCheckBox(flowPane5, "getManyJoinAll" + un, false);
            }
        }
        vBox.getChildren().add(flowPane5);

        FlowPane flowPane6 = createFlowPane();
        vBox.getChildren().addAll(new Label("分页多条件查询:" ), new Separator(Orientation.HORIZONTAL));
        boolean isCreate = !table.isMiddle();
        createCheckBox(flowPane6, "find", isCreate);
        if (table.getForeignKeys().size() > 0) {
            createCheckBox(flowPane6, "findJoinBy", isCreate);
        }
        vBox.getChildren().add(flowPane6);
        vBox.getChildren().add(new Separator(Orientation.HORIZONTAL));
        return vBox;
    }

    private void createCheckBox(FlowPane flowPane, String functionName, boolean isCreate) {
        Map<String, Boolean> funs = MakerConfig.getInstance().getFunctionMap(tableName);
        if (funs != null) {
            isCreate = funs.containsKey(functionName) ? funs.get(functionName) : isCreate;
        }
        CheckBox cb = new CheckBox(functionName);
        cb.setSelected(isCreate);
        cb.setPrefWidth(280);
        flowPane.getChildren().add(cb);
        functionMap.put(functionName, cb);
    }

    private FlowPane createFlowPane() {
        FlowPane flowPane = new FlowPane();
        flowPane.setStyle("-fx-background-color: white");
        flowPane.setHgap(50d);
        flowPane.setVgap(10);
        flowPane.setPadding(new Insets(10, 10, 10, 10));
        return flowPane;
    }
}
